﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading;

namespace Apewer
{

    /// <summary>Cron 特性，默认间隔为 60 秒。</summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    public sealed class CronAttribute : Attribute, IToJson
    {

        private int _mode = 0;
        private int _seconds = 0;
        private CronCycle _cycle = CronCycle.Once;

        /// <summary>获取秒数。</summary>
        public int Seconds { get { return _seconds; } }

        /// <summary>创建 Cron 特性，可指定两次 Cron 执行的间隔秒数。</summary>
        public CronAttribute(int seconds = 60)
        {
            _mode = 1;
            _seconds = seconds;
            if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds));
        }

        /// <summary>创建 Cron 特性，在每个周期中延迟指定秒数后执行。</summary>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public CronAttribute(CronCycle cycle, int seconds = 0)
        {
            _mode = 2;
            _cycle = cycle;
            _seconds = seconds;
            if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds));
        }

        /// <summary>生成 Json 对象。</summary>
        public Json ToJson()
        {
            var mode = _mode == 1 ? "interval" : "cycle";
            var type = _type == null ? null : _type.FullName;

            var json = Json.NewObject();
            json.SetProperty("type", type);
            json.SetProperty("mode", mode);
            if (_mode == 2) json.SetProperty("cycle", _cycle.ToString().ToLower());
            json.SetProperty("second", _seconds);
            return json;
        }

        #region Payload

        // 传入。
        Type _type = null;
        Logger _logger = null;
        bool _event = false;

        // 实时。
        Thread _thread = null;
        bool _alive = false;

        void Run()
        {
            _alive = true;
            _thread = new Thread(Payload);
            _thread.IsBackground = false;
            _thread.Start();
        }

        // 当前进程睡眠。
        static void Sleep() => Thread.Sleep(500);

        // 线程负载。
        void Payload()
        {
            // 基于间隔时间调用。
            if (_mode == 1)
            {
                // 初始值为最小值，保证第一次启动时必定运行。
                var next = DateTime.MinValue;
                while (true)
                {
                    if (_break) break;
                    if (_now < next)
                    {
                        Sleep();
                        if (_break) break;
                        continue;
                    }

                    Invoke();
                    next = DateTime.Now.AddSeconds(_seconds);
                }
            }

            // 仅执行一次，延迟启动。
            if (_mode == 2 && _cycle == CronCycle.Once)
            {
                var next = DateTime.Now.AddSeconds(_seconds);
                while (_now < next)
                {
                    if (_break) break;
                    Sleep();
                    if (_break) break;
                }
                Invoke();
            }

            // 基于周期调用。
            if (_mode == 2 && _cycle != CronCycle.Once)
            {
                // 计算当前期间的运行时间。
                DateTime next = DateTime.Now;
                switch (_cycle)
                {
                    case CronCycle.Minute:
                        next = new DateTime(next.Year, next.Month, next.Day, next.Hour, next.Minute, 0, 0);
                        break;
                    case CronCycle.Hour:
                        next = new DateTime(next.Year, next.Month, next.Day, next.Hour, 0, 0, 0);
                        break;
                    case CronCycle.Day:
                        next = new DateTime(next.Year, next.Month, next.Day, 0, 0, 0, 0);
                        break;
                    case CronCycle.Week:
                        next = new DateTime(next.Year, next.Month, next.Day, 0, 0, 0, 0).AddDays(-(int)next.DayOfWeek);
                        break;
                }
                if (_seconds > 0) next = next.AddSeconds(_seconds);

                // 启动循环。
                while (true)
                {
                    if (_break) break;
                    if (_now < next)
                    {
                        Sleep();
                        if (_break) break;
                        continue;
                    }

                    Invoke();
                    switch (_cycle)
                    {
                        case CronCycle.Minute:
                            next = next.AddMinutes(1);
                            break;
                        case CronCycle.Hour:
                            next = next.AddHours(1);
                            break;
                        case CronCycle.Day:
                            next = next.AddDays(1);
                            break;
                        case CronCycle.Week:
                            next = next.AddDays(7);
                            break;
                        default:
                            _alive = false;
                            return;
                    }
                }
            }

            _alive = false;
        }

        void Invoke()
        {
            var caption = CaptionAttribute.Parse(_type);
            var sender = caption?.Title;
            if (sender.IsEmpty()) sender = _type.Name;
            if (_event) _logger.Text(sender, "Beginning");
            var instance = null as object;
            try
            {
                instance = Activator.CreateInstance(_type);
                if (_event) _logger.Text(sender, "Ended");
            }
            catch (Exception ex)
            {
                _logger.Exception(ex.InnerException, sender);
            }
            RuntimeUtility.Dispose(instance);
        }

        #endregion

        #region CronInvoker

        static object _start = new object();
        static CronAttribute[] _crons = null;
        static bool _break = false;
        static DateTime _now;

        static CronAttribute[] Init(IEnumerable<Assembly> assemblies, Logger logger, bool logEvent)
        {
            var ab = new ArrayBuilder<CronAttribute>();
            if (assemblies == null) assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assembly in assemblies)
            {
                var types = assembly.GetTypes();
                foreach (var type in types)
                {
                    if (!type.IsClass) continue;
                    if (type.IsAbstract) continue;
                    if (!RuntimeUtility.CanNew(type)) continue;

                    var attributes = type.GetCustomAttributes(false);
                    if (attributes == null) continue;
                    foreach (var attribute in attributes)
                    {
                        var cron = attribute as CronAttribute;
                        if (cron == null) continue;
                        cron._type = type;
                        cron._logger = logger;
                        cron._event = logEvent;
                        ab.Add(cron);
                    }
                }
            }
            var array = ab.Export();
            return array;
        }

        /// <summary>开始 Cron 调用（阻塞当前线程）。</summary>
        /// <param name="assemblies">包含 Cron 的程序集，不指定此参数时将在 AppDomain 中搜索。</param>
        /// <param name="logger">日志记录程序，不指定此参数时将使用 Logger.Default。</param>
        /// <param name="logEvent">对 Cron 开始和结束记录日志。</param>
        /// <remarks>
        /// 参数<br />
        /// - assemblies: 包含 Cron 的程序集，不指定此参数时将在 AppDomain 中搜索；<br />
        /// - logger: 日志记录程序，不指定此参数时将使用 Logger.Default；<br />
        /// - logEvent：对 Cron 开始和结束记录日志。
        /// </remarks>
        public static void Start(IEnumerable<Assembly> assemblies = null, Logger logger = null, bool logEvent = true)
        {
            if (logger == null) logger = Logger.Default;
            lock (_start)
            {
                // 初始化。
                _now = DateTime.Now;
                _crons = Init(assemblies, logger, logEvent);
                if (_crons.Length < 1)
                {
                    logger.Error(nameof(CronAttribute), "没有找到带有 Cron 特性的类型。");
                    return;
                }

                // 启动线程。
                Console.CancelKeyPress += (s, e) =>
                {
                    _break = true;
                    e.Cancel = true;
                };
                logger.Text(nameof(CronAttribute), $"启动 {_crons.Length} 个 Cron 线程。");
                foreach (var cron in _crons) cron.Run();

                // 监视退出状态。
                while (true)
                {
                    Thread.Sleep(300);
                    _now = DateTime.Now;
                    var alive = 0;
                    for (var i = 0; i < _crons.Length; i++) if (_crons[i]._alive) alive += 1;
                    if (alive < 1) break;
                }
                logger.Text(nameof(CronAttribute), "所有 Cron 已结束。");
            }
        }

        /// <summary>打断 Cron 循环，不打断正在执行的 Cron。</summary>
        public static void Break()
        {
            _break = true;
        }

        /// <summary>获取状态，指示打断 Cron 循环。</summary>
        public static bool Breaking { get => _break; }

        #endregion

    }

}
